package com.shijie.sjoj.service.box;

import com.shijie.sjoj.model.component.ParamsTypeFormat;
import com.shijie.sjoj.model.exception.JudgeException;
import com.shijie.sjoj.model.pojo.base.CodeType;
import com.shijie.sjoj.model.pojo.base.JudgedInfo;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.springframework.stereotype.Component;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * JavaScript语言判题机
 *
 * @author ZSJ
 */
@Slf4j
@Component
@NoArgsConstructor
@JudgeCodeType(value = CodeType.JavaScript)
public class JudgeWorkerJavaScript implements JudgeWorker {

    /**
     * 语言类型
     */
    private static final String Language = "js";

    @Override
    public JudgedInfo working(String code, CodeType codeType,
                              Class<?>[] argsType, List<List<?>> args,
                              Class<?> outcomeType, List<List<?>> outcome) {
        // 获取函数名
        String functionName = getFunctionName(code);
        // 返回执行结果
        return this.execute(code, functionName, argsType, args, outcomeType, outcome);
    }

    /**
     * 获取函数名，主函数必须在第一个位置
     *
     * @param code 代码
     * @return String
     */
    private String getFunctionName(String code) {
        // 默认的函数名
        String functionName = "solving";
        // 分割代码
        String[] split = code.split(" ");
        // 获取函数名
        for (int i = 0; i < split.length; i++) {
            if (split[i].equals("const")) {
                do i++;
                while (split[i].isBlank());
                functionName = split[i].split("=")[0].trim();
                break;
            }
        }
        // 返回函数名
        return functionName;
    }

    /**
     * 执行代码，比较原型结果和代码输出结果
     *
     * @param code        代码
     * @param funcName    函数名
     * @param argsType    原型入参类型
     * @param args        原型入参
     * @param outcomeType 原型结果类型
     * @param outcome     原型结果
     * @return JudgedInfo
     */
    private JudgedInfo execute(String code, String funcName,
                               Class<?>[] argsType, List<List<?>> args,
                               Class<?> outcomeType, List<List<?>> outcome) {

        // 初始化使用时间和内存
        long useTimeNano = 1L; //纳秒 1ms = 1_000_000ns
        long useMemoryByte = 1L; //字节
        // 获取内存管理器
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        // 初始化代码输出的结果
        List<List<?>> executeResult = new ArrayList<>();
        // 开始执行编译
        // 构建环境上下文并设置权限
        try (Context context = Context.newBuilder(Language)
                .allowAllAccess(false) // 所有权限
                .allowIO(IOAccess.NONE) // IO权限
                .allowHostAccess(HostAccess.ALL) // 通信权限
                .build()) {
            // 绑定语言和代码
            Value ignore = context.eval(Language, code);
            // 绑定语言和方法
            Value member = context.getBindings(Language).getMember(funcName);
            // 参数格式化
            for (List<?> list : args) {
                // 入参列表转为数组
                Object[] ias = list.toArray();
                // 入参格式化
                for (int i = 0; i < ias.length; i++) {
                    ias[i] = ParamsTypeFormat.argvFormat(ias[i], argsType[i]);
                }
                // 开始方法计时与内存
                long beginT = System.nanoTime();
                MemoryUsage beginMUsage = memoryBean.getHeapMemoryUsage();
                // 执行方法获取输出结果
                Value value = member.execute(ias);
                // 结束方法计时与内存
                long endT = System.nanoTime();
                MemoryUsage endMUsage = memoryBean.getHeapMemoryUsage();
                useTimeNano += (endT - beginT);
                useMemoryByte += (endMUsage.getUsed() - beginMUsage.getUsed());
                // 格式化并添加输出结果，此时输出结果直接存储可能为地址（数组等类型），需格式化
                executeResult.add(List.of(this.valueFormat(value, outcomeType)));
            }
        } catch (Exception e) {
            log.error(e.toString());
            throw JudgeException.throwError("js编译失败");
        }
        // 进行比较
        JudgedInfo judgedInfo = JudgeWorker.compare(outcomeType, outcome, executeResult);
        // 结果不正确
        if (!judgedInfo.getFlag()) {
            return judgedInfo;
        }
        // 结果正确
        Integer useTimeMill = Integer.valueOf(Long.toString(useTimeNano / 1_000_000)); //ms
        Integer useMemoryKb = Integer.valueOf(Long.toString(useMemoryByte / 1024)); //kb
        return JudgedInfo.of(useTimeMill, useMemoryKb);
    }

    /**
     * 执行结果格式化
     *
     * @param value 值
     * @param type  类型
     * @return Object
     */
    private Object valueFormat(Value value, Class<?> type) {
        // 校验null
        if (null == value || value.isNull()) {
            return null;
        }
        // 数组/集合类型
        if (type.isArray() || type.equals(List.class)) {
            return Arrays.asList(value.as(Object[].class));
        }
        // 其他类型
        return value.as(type);
    }


}
